
#include "zen_vap_label.h"
#include "zen_utf8.h"
#include "zen_ticker.h"

namespace Zen { namespace Vap {

	FontLabel::FontLabel(std::string const & name)
	: Sprite(name)
	{
	}

	void TextureWithRender::setFontRender(std::shared_ptr<FontRender> render)
	{
		m_render = render;
		m_is_render_dirty = true;
	}

	void TextureWithRender::setImageEffect(ImageEffectFunction effect) {
		m_image_effect = effect;
		m_is_image_dirty = true;
	}

	void TextureWithRender::setUnderlineInPixels(int width)
	{
		if(width == m_underline_px) return;
		m_underline_px = width;
		m_is_render_dirty = true;
	}

	bool TextureWithRender::clearRenderDirty()
	{
		if(m_is_render_dirty)
		{
			m_is_render_dirty = false;

			if(!m_render)
			{
				m_is_texture_empty = true;
				return true;
			}

			if(m_render->width() && m_render->height())
			{
				auto set = FontRender::RenderSetting();
				set.underline = m_underline_px;
				set.fill = 0;

				m_image = m_render->renderImage(0, 0, set);
			}
			else
			{
				m_image = nullptr;
			}
			m_is_image_dirty = true;
		}
		if(m_is_image_dirty)
		{
			m_is_image_dirty = false;

			auto image = (m_image_effect? m_image_effect(m_image) : m_image);

			if(!image || image->format() == ePixel::None) {

				m_is_texture_empty = true;
			}
			else
			{
				// re create
				m_texture_rendered = Texture::Create();
				m_texture_rendered->set(image);
				m_is_texture_empty = false;
			}

			return true;
		}
		return false;
	}

	SharedTexture TextureWithRender::getTextureRendered() {
		if(m_is_texture_empty) return nullptr;
		return m_texture_rendered;
	}

/// with font

	void FontLabel::setMaxWidthInPixels(int pixels)
	{
		if(pixels == m_max_width_px) return;
		m_max_width_px = pixels;
		m_is_page_dirty = true;
	}

	void FontLabel::setCharsSpacingInPixels(int pixels)
	{
		if(pixels == m_char_spacing_px) return;
		m_char_spacing_px = pixels;
		m_is_page_dirty = true;
	}

	void FontLabel::setLinesSpacingInPixels(int pixels)
	{
		if(pixels == m_line_spacing_px) return;
		m_line_spacing_px = pixels;
		m_is_page_dirty = true;
	}

	void FontLabel::setTextAlignment(float alignment)
	{
		if(alignment == m_alignment) return;
		m_alignment = alignment;
		m_is_page_dirty = true;
	}

	void FontLabel::setText(std::string const & text)
	{
		if(m_text == text) return;
		m_text = text;
		m_is_page_dirty = true;
	}

	bool FontLabel::clearFontDirty()
	{
		if(m_is_page_dirty || m_is_font_brush_dirty) {

			m_is_page_dirty = false;
			m_is_font_brush_dirty = false;

			auto text32 = Zen::ToU32(m_text);

			auto style = FontRender::Style();
			style.alignment = m_alignment;
			style.word_space = m_char_spacing_px;
			style.line_space = m_line_spacing_px;
			style.width_limit = m_max_width_px;

			auto render = FontRender::Create(m_font, text32, style);

			this->setFontRender(render);

			return this->clearRenderDirty();
		}
		return false;
	}
/// label

	void FontLabel::setFontBrush(std::shared_ptr<FontBrush> font)
	{
		if(font == m_font) return;
		m_font = font;
		m_is_font_brush_dirty = true;
	}
	void FontLabel::setFontSize(float size)
	{
		if(size == m_font_size) return;
		m_font_size = size;
		m_is_font_size_dirty = true;
	}

	void FontLabel::load()
	{

	}
	void FontLabel::ready()
	{
		if(this->clearFontDirty())
		{
			this->setTexture(getTextureRendered());
			m_is_font_size_dirty = true;
		}
		if(m_is_font_size_dirty) {

			if(getFontBrush())
			{
				float scale = m_font_size / getFontBrush()->getHeight();
				this->setTextureScale({scale, scale});
			}
			m_is_font_size_dirty = false;
		}
		Sprite::ready();
	}

	void FontLabel::draw()
	{
		Sprite::draw();
	}
}}


/// Tilemap


namespace Zen { namespace Vap {
	void TilemapFont::load(SharedTexture texture, Rect4f area, char32_t start_code, int x_count, int y_count)
	{
		m_texture = texture;
		m_texture_area = area;
		m_start_code = start_code;
		m_count = x_count * y_count;
		m_x_count = x_count;
		m_y_count = y_count;

		auto dx = area.w / x_count;
		auto dy = area.h / y_count;
		m_tile_char_coords.clear();

		TileCharCoords c;

		auto s = m_texture->size();
		c.view_size.w = s.w * dx;
		c.view_size.h = s.h * dy;

		auto code = start_code;

		float py = area.y;
		for(int y = 0; y < y_count; ++y, py += dy) {
			float px = area.x;
			for(int x = 0; x < x_count; ++x, px += dx) {
				c.tex_coord00 = { px, py + dy };
				c.tex_coord11 = { px + dx, py };

				m_tile_char_coords[code++] = c;
			}
		}
	}


	TileCharCoords const * TilemapFont::getTile(char32_t unicode)
	{
		auto iter = m_tile_char_coords.find(unicode);
		if(iter == m_tile_char_coords.end()) {
			return nullptr;
		}
		return &(iter->second);
	}

	TilemapLabel::TilemapLabel()
	{

	}
	void TilemapLabel::setTileMap(std::shared_ptr<TilemapFont> map)
	{
		mTileMap = map;
		m_is_font_dirty = true;
	}


	void TilemapLabel::setCharSpacing(float x) {
		m_spacing_x = x;
		m_is_font_dirty = true;
	}
	void TilemapLabel::setContentSize(Size2 size) {
		m_fixed_size = size;
		m_is_fixed_size = true;
		if(!m_is_font_dirty) {
			this->setScale2_({m_fixed_size.w, m_fixed_size.h});
		}
	}
	void TilemapLabel::setContentSize() {
		m_is_font_dirty = false;

		if(!m_is_font_dirty) {
			this->setScale2_({m_auto_size.w, m_auto_size.h});
		}
	}

	void TilemapLabel::setContentSizeWithWidth(float w) {
		this->updateContent();
		this->setContentSize({ w, m_auto_size.h / m_auto_size.w * w });
	}

	void TilemapLabel::setContentSizeWithHeight(float h) {
		this->updateContent();
		this->setContentSize({ m_auto_size.w / m_auto_size.h * h, h });
	}

	Size2 TilemapLabel::getContentSize() {
		if(m_is_fixed_size) {
			return m_fixed_size;
		}
		this->updateContent();
		return m_auto_size;
	}
	void TilemapLabel::setText(const std::string &s) {
		if(m_text == s) return;
		m_text = s;
		m_text_u32 = Zen::ToU32(s);
		m_is_font_dirty = true;
	}

	void TilemapLabel::updateContent()
	{
		if(!m_is_font_dirty) return;

		if(!mTileMap) {
			m_auto_size = {0, 0};
			return;
		}

		m_is_font_dirty = false;

		m_is_flip_dirty = false;

		float h = 0;
//		FigureConte
		m_vertexes.clear();

		float x = 0;

		for(auto c : m_text_u32) {
			auto tile = mTileMap->getTile(c);
			if(tile == nullptr) {
				Zen::LogD("not found tile map char: [%d]", (int)c);
				continue;
			}
			auto t00 = tile->tex_coord00;
			auto t11 = tile->tex_coord11;
//			if(m_is_flip_x) std::swap(t00.x, t11.x);
//			if(m_is_flip_y) std::swap(t00.y, t11.y);

			auto t01 = Vec2{ t00.x, t11.y };
			auto t10 = Vec2{ t11.x, t00.y };
			if(m_vertexes.size()) {
				x += m_spacing_x;
			}
			if(tile->view_size.h > h) h = tile->view_size.h;

			auto c00 = Vec2{ x, 0};
			auto c11 = Vec2{ x + tile->view_size.w, tile->view_size.h };
			auto c01 = Vec2{ c00.x, c11.y };
			auto c10 = Vec2{ c11.x, c00.y };

			m_vertexes.push_back(GL::VertexDataTexture{ c00, t00 });
			m_vertexes.push_back(GL::VertexDataTexture{ c10, t10 });
			m_vertexes.push_back(GL::VertexDataTexture{ c11, t11 });
			m_vertexes.push_back(GL::VertexDataTexture{ c00, t00 });
			m_vertexes.push_back(GL::VertexDataTexture{ c11, t11 });
			m_vertexes.push_back(GL::VertexDataTexture{ c01, t01 });

			x += tile->view_size.w;
		}
		m_auto_size = { x, h };

		if(m_is_flip_x) {
			if(!m_is_flip_y)
			{
				for(auto & v : m_vertexes) {
					v.coord.x = 1 - v.coord.x / x;
					v.coord.y /= h;
				}
			}
			else
			{
				for(auto & v : m_vertexes) {
					v.coord.x = 1 - v.coord.x / x;
					v.coord.y = 1 - v.coord.y / h;
				}
			}
		}
		else {
			if(!m_is_flip_y) {
				for(auto & v : m_vertexes) {
					v.coord.x /= x;
					v.coord.y /= h;
				}
			}
			else
			{

				for(auto & v : m_vertexes) {
					v.coord.x /= x;
					v.coord.y = 1 - v.coord.y / h;
				}
			}
		}

		if(m_is_fixed_size) {
			this->setScale2_({m_fixed_size.w, m_fixed_size.h});
		}
		else {
			this->setScale2_({m_auto_size.w, m_auto_size.h});
		}
		m_is_gl_buffer_dirty = true;
	}

	void TilemapLabel::ready()
	{
		if(m_is_flip_dirty) {
			m_is_font_dirty = true;
		}
		this->updateContent();
	}

	void TilemapLabel::draw() {
		if(mTileMap == nullptr) return;

//		FigureTexture::draw();
		if(!mTileMap->getTexture()) return;

		if(m_is_gl_buffer_dirty) {
			m_gl_buffer.updateBuffer(m_vertexes.data(), m_vertexes.size());
			m_is_gl_buffer_dirty = false;
		}

		auto alpha = DrawStack::Get()->getTopAlpha() * this->getAlpha();

		auto texture = mTileMap->getTexture();

		auto fmt = texture?texture->format() : ePixel::None;

		auto kit = m_shader_selector? m_shader_selector(m_greyfied, fmt) : GL::ShaderKitTexture::DefaultSelector(m_greyfied, fmt);

		Helper::Get()->performBlend(m_blend);

		kit->active();

		m_gl_buffer.bind();

		using Type = GL::DrawBufferTexture::VertexData;
		kit->bindAttCoord(2, GL::eType::Float, 0, sizeof(Type), Type::OffCoord);
		kit->bindAttTexCoord(2, GL::eType::Float, 0, sizeof(Type), Type::OffTexCoord);

		kit->setUniMat4(this->getMatrix());
		kit->setUniColor(Vec4{ m_color.red, m_color.green, m_color.blue, m_color.alpha*alpha });
		kit->setUniTexRect(m_texture_rect_converted);
		if(texture) kit->setUniSampler(1, texture->buffer().getID());
		if(m_greyfied) kit->setUniGreyFactor(m_grey_factor);

		kit->prepare();
		kit->render(GL::eVMode::Triangle, 0, m_gl_buffer.count());
	}
}}

